package service

import (
	"context"
	"mini-app/config"
	"mini-app/internal/app/grpc/common"
	"mini-app/internal/dal"
	"mini-app/internal/dao"
	"mini-app/internal/define"
	"mini-app/internal/models"
	"strings"

	"192.168.1.75/go-pkg/errorx"
	"github.com/samber/lo"
)

// PromotionChannel 小程序渠道
type PromotionChannel struct{}

// Add 新增渠道
func (PromotionChannel) Add(ctx context.Context, name string, robotIDs []string) (channelID string, err error) {
	q := dal.Q

	if name == "" {
		return "", errorx.New("渠道名称不能为空", -1)
	}

	if len(robotIDs) == 0 || robotIDs[0] == "" {
		return "", errorx.New("设备不能为空", -1)
	}

	if (dao.PromotionChannel{}).ExistsName(ctx, q, name) {
		return "", errorx.New("渠道名称已存在", -1)
	}

	// 设备重复分配检查
	assignedRobotIDs, err := PromotionChannel{}.AssignedRebotIDs(ctx, "")
	if err != nil {
		return "", err
	}
	if len(lo.Intersect(assignedRobotIDs, robotIDs)) > 0 {
		return "", errorx.New("设备不能重复分配", -1)
	}

	err = q.Transaction(func(tx *dal.Query) error {
		var err error
		channelID, err = dao.PromotionChannel{}.Add(ctx, tx, name, int64(len(robotIDs)))
		if err != nil {
			return err
		}
		// 更新渠道与设备和位置的关联信息
		err = PromotionChannel{}.updateChannelDevice(ctx, tx, channelID, robotIDs)
		if err != nil {
			return err
		}
		return nil
	})
	return channelID, err
}

// Update 更新渠道
func (PromotionChannel) Update(ctx context.Context, id, name string, robotIDs []string) error {
	q := dal.Q
	detail := dao.PromotionChannel{}.Detail(ctx, q, id)
	if detail == nil {
		return errorx.New("无效的渠道", -1)
	}
	if name != "" && (dao.PromotionChannel{}).ExistsName(ctx, q, name) && detail.Name != name {
		return errorx.New("渠道名称已存在", -1)
	}
	// 设备重复分配检查
	assignedRobotIDs, err := PromotionChannel{}.AssignedRebotIDs(ctx, id)
	if err != nil {
		return err
	}
	if len(lo.Intersect(assignedRobotIDs, robotIDs)) > 0 {
		return errorx.New("设备不能重复分配", -1)
	}
	// 更新
	err = q.Transaction(func(tx *dal.Query) error {
		// 重置渠道关联的设备和位置
		if len(robotIDs) > 0 {
			err := PromotionChannel{}.resetChannelDevice(ctx, tx, id)
			if err != nil {
				return err
			}
			// 更新渠道信息
			err = dao.PromotionChannel{}.Update(ctx, tx, id, name, int64(len(robotIDs)))
			if err != nil {
				return err
			}
			// 更新渠道与设备和位置的关联信息
			err = PromotionChannel{}.updateChannelDevice(ctx, tx, id, robotIDs)
			if err != nil {
				return err
			}
		}
		return nil
	})
	return err
}

// Delete 删除渠道
func (PromotionChannel) Delete(ctx context.Context, id string) error {
	q := dal.Q
	err := q.Transaction(func(tx *dal.Query) error {
		// 删除渠道信息
		err := dao.PromotionChannel{}.Delete(ctx, tx, id)
		if err != nil {
			return err
		}
		// 重置渠道设备
		err = PromotionChannel{}.resetChannelDevice(ctx, tx, id)
		if err != nil {
			return err
		}

		return nil
	})
	return err
}

type PromotionChannelListT struct {
	ID          string   `json:"id"`
	Name        string   `json:"name"`
	TotalMember int64    `json:"total_member"`
	TotalDevice int64    `json:"total_device"`
	RobotIDs    []string `json:"robot_ids"`
	Locations   string   `json:"locations"`
	CreatedAt   int64    `json:"created_at"`
}

// List 渠道列表
func (PromotionChannel) List(ctx context.Context, name string, page, limit int) (list []PromotionChannelListT, total int64) {
	q := dal.Q
	records, total, _ := dao.PromotionChannel{}.List(ctx, q, name, page, limit)
	for _, record := range records {
		// 获取关联的设备
		robotIDs, _ := dao.ObjAssociation{}.GetAssociationID(ctx, q, define.AssociationPromotionChannel, record.ID, "")
		// 获取覆盖区域
		locationNames := []string{}
		locationIDs, _ := dao.ObjAssociation{}.GetAssociationID(ctx, q, define.AssociationPromotionChannelLocation, record.ID, "")
		locationNameMap := common.GRPC{}.GetLocationName(ctx, locationIDs)
		for _, name := range locationNameMap {
			locationNames = append(locationNames, name)
		}
		list = append(list, PromotionChannelListT{
			ID:          record.ID,
			Name:        record.Name,
			TotalMember: record.TotalMember,
			TotalDevice: record.TotalDevice,
			RobotIDs:    robotIDs,
			Locations:   strings.Join(locationNames, ","),
			CreatedAt:   record.CreatedAt,
		})
	}
	return
}

// AssignedRebotIDs 已被分配的机器
// 已经被分配的机器不能再分配给其他渠道
// 当编辑时候channelID必须
func (PromotionChannel) AssignedRebotIDs(ctx context.Context, channelID string) (robotIDs []string, err error) {
	// 获取全部已经被分配的机器id
	records, err := dao.ObjAssociation{}.GetAssociationRecords(ctx, dal.Q, define.AssociationPromotionChannel)
	if err != nil {
		return []string{}, errorx.New("获取已分配设备失败", -1)
	}
	for _, record := range records {
		robotIDs = append(robotIDs, record.AssociationID)
	}
	// 新增操作时，返回全部
	if channelID == "" {
		return robotIDs, nil
	} else { // 编辑操作时，返回编辑器渠道之外的已分配设备
		channelRobotIDs, err := dao.ObjAssociation{}.GetAssociationID(ctx, dal.Q, define.AssociationPromotionChannel, channelID, "")
		if err != nil {
			return []string{}, errorx.New("获取已分配设备失败", -1)
		}
		robotIDs, _ = lo.Difference(robotIDs, channelRobotIDs)
		return robotIDs, nil
	}
}

// GetChannelByRobotID 获取设备的渠道id
func (PromotionChannel) GetChannelByRobotID(ctx context.Context, query *dal.Query, robotID string) (channelID string, err error) {
	associationIDs, _ := dao.ObjAssociation{}.GetAssociationID(ctx, dal.Q, define.AssociationPromotionChannel, "", robotID)
	if len(associationIDs) != 1 {
		return "", errorx.New("获取渠道id失败", -1)
	}
	return associationIDs[0], nil
}

// resetChannelDevice 重置渠道设备
func (PromotionChannel) resetChannelDevice(ctx context.Context, query *dal.Query, channelID string) error {
	err := dao.ObjAssociation{}.Delete(ctx, query, define.AssociationPromotionChannel, channelID, "")
	if err != nil {
		return err
	}
	err = dao.ObjAssociation{}.Delete(ctx, query, define.AssociationPromotionChannelLocation, channelID, "")
	if err != nil {
		return err
	}
	return nil
}

// updateChannelDevice 更新渠道关联的设备信息
func (PromotionChannel) updateChannelDevice(ctx context.Context, query *dal.Query, channelID string, robotIDs []string) error {
	robotIDs = lo.Uniq(robotIDs)
	// 更新渠道与设备的关联信息
	{
		var objAssociations []*models.ObjAssociation
		for _, robotID := range robotIDs {
			if robotID == "" {
				continue
			}
			objAssociations = append(objAssociations, &models.ObjAssociation{
				AssociationType: int(define.AssociationPromotionChannel),
				ObjID:           channelID,
				AssociationID:   robotID,
			})
		}
		if len(objAssociations) == 0 {
			return errorx.New("渠道设备不能为空", -1)
		}
		_, err := dao.ObjAssociation{}.Add(ctx, query, objAssociations, 200, nil, nil)
		if err != nil {
			return err
		}
	}
	// 更新渠道与位置的关联信息
	{
		// 获取对应商户的设备列表
		rsp, err := common.GRPC{}.DeviceList(ctx, "", "", "", "", config.Config.MerchantID, 0, "", 0, 0)
		if err != nil || len(rsp.List) == 0 {
			return errorx.New("获取设备信息失败", -1)
		}
		// 创建设备与位置的map
		var robotLocationMap = make(map[string]string)
		for _, record := range rsp.List {
			robotLocationMap[record.RobotId] = record.LocationId
		}
		// 获取全部去重的位置
		var locationIDs []string
		for _, robotID := range robotIDs {
			if robotID == "" {
				continue
			}
			if robotLocationMap[robotID] != "" {
				locationIDs = append(locationIDs, robotLocationMap[robotID])
			}
		}
		locationIDs = lo.Uniq(locationIDs)
		// 渠道与位置的关联
		var objAssociations []*models.ObjAssociation
		for _, locationID := range locationIDs {
			objAssociations = append(objAssociations, &models.ObjAssociation{
				AssociationType: int(define.AssociationPromotionChannelLocation),
				ObjID:           channelID,
				AssociationID:   locationID,
			})
		}
		_, err = dao.ObjAssociation{}.Add(ctx, query, objAssociations, 200, nil, nil)
		if err != nil {
			return err
		}
	}
	return nil
}
